home *** CD-ROM | disk | FTP | other *** search
/ POINT Software Programming / PPROG1.ISO / pascal / swag / textfile.swg / 0016_Reading Text Backwards.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  1993-08-27  |  5.0 KB  |  215 lines

  1. {
  2. LARS FOSDAL
  3.  
  4. > I'm working on a project where Text Records are appended to a disk File
  5. > at regular intervals.  I'd like to position the Pointer at the end of the
  6. > File and read the line ending at the end of File into a null-terminated
  7. > String (BP7).
  8. > I can think of a couple of ways to implement this quickly:  1) prepend
  9. > the Record to the File instead of appending, and 2) Write a fast driver
  10. > to do the backwards reading For me.
  11.  
  12. 1) Prepending instead of appending...
  13.    I think you might run into some problems With this...
  14.    To prepend a line, you must first read the entire File,
  15.    then move to the start of the File again, Write the new Record,
  16.    and finally Write back all the Records you first read.
  17.    The overhead would become enormous if the File was large.
  18.  
  19. 2) Fast driver For backwards reading...  Aha!
  20.    This is the way to do it.
  21.  
  22.    Below you will find the source of a "tail" Program.
  23.    I wrote it because I needed to check the status of some log Files,
  24.    and I didn't want to go through the entire File every time, as the
  25.    Files could grow quite large.
  26.  
  27.    It is currently limited to 255 Chars per line, but that
  28.    can easily be fixed (see the Limit Const).
  29.  
  30.    Although it's not an exact solution to your problem, it will show you
  31.    how to do "backwards" reading.
  32. }
  33.  
  34. Program Tail;
  35. {
  36.   Shows the tailing lines of a Text File.
  37.  
  38.   Syntax: TAIL [d:\path]Filespec.ext [-<lines>]
  39.           Default number of lines is 10.
  40.  
  41.           "TAIL Filename -20" will show the 20 last lines
  42.  
  43.   Written by Lars Fosdal, 1993
  44.   Released to the Public Domain by Lars Fosdal, 1993
  45. }
  46.  
  47. Uses
  48.   Dos, Objects, Strings;
  49.  
  50. Const
  51.   MaxBufSize = 32000;
  52.  
  53. Type
  54.   pBuffer = ^TBuffer;
  55.   TBuffer = Array[0..MaxBufSize-1] of Char;
  56.  
  57.   pRawStrCollection = ^TRawStrCollection;
  58.   TRawStrCollection = Object(TCollection)
  59.     Procedure FreeItem(Item : Pointer); VIRTUAL;
  60.   end;
  61.  
  62.   CharSet = Set of Char;
  63.  
  64. Var
  65.   r, l, e : Integer;
  66.  
  67.  
  68. Procedure TRawStrCollection.FreeItem(Item : Pointer);
  69. begin
  70.   if Item <> nil then
  71.     StrDispose(pChar(Item));
  72. end;
  73.  
  74. Function ShowTail(FileName : String; n : Integer) : Integer;
  75. Const
  76.   Limit = 255;
  77. Var
  78.   lines   : pRawStrCollection;
  79.   fm      : Byte;
  80.   f       : File;
  81.   fs, fp  : LongInt;
  82.   MaxRead : Word;
  83.   Buf     : pBuffer;
  84.   lc, ix,
  85.   ex      : Integer;
  86.   sp      : Array [0..Limit] of Char;
  87.  
  88.   Procedure DumpLine(p : pChar); Far;
  89.   begin
  90.     if p^ = #255 then
  91.       Writeln
  92.     else
  93.       Writeln(p);
  94.   end;
  95.  
  96. begin
  97.   lines := nil;
  98.   fm    := FileMode;
  99.   FileMode := $40; {Read-only, deny none}
  100.   Assign(f, FileName);
  101.   Reset(f, 1);
  102.   lc := IOResult;
  103.  
  104.   if lc = 0 then
  105.   begin
  106.     New(Buf);
  107.  
  108.     fs := FileSize(f); {First, let's find out how much to read}
  109.     fp := fs - MaxBufSize;
  110.     if fp < 0 then
  111.       fp := 0;
  112.  
  113.     Seek(f, fp); {Then, read it}
  114.     BlockRead(f, Buf^, MaxBufSize, MaxRead);
  115.     Close(f);
  116.  
  117.     if MaxRead > 0 then
  118.     begin
  119.       New(Lines, Init(n, 10));
  120.       ix := MaxRead - 1;
  121.  
  122.       if Buf^[ix] = ^J then
  123.         Dec(ix);
  124.       if (ix > 0) and (Buf^[ix] = ^M) then
  125.         Dec(ix); {Skip trailing line break}
  126.  
  127.       While (lc < n) and (ix > 0) DO
  128.       begin
  129.         ex := ix;
  130.         FillChar(sp, SizeOf(sp), 0);
  131.  
  132.         While (ix > 0) and not (Buf^[ix] = ^J) DO
  133.           Dec(ix);
  134.  
  135.         if ex - ix <= Limit then
  136.         {if no break was found Within limit, it's no txt File}
  137.         begin
  138.           if ix = ex then
  139.             sp[0] := #255 {Pad empty lines to avoid zero-length pChar}
  140.           else
  141.             StrLCopy(sp, @Buf^[ix + 1], ex - ix);
  142.           Inc(lc);
  143.  
  144.           Lines^.AtInsert(0, StrNew(sp));
  145.  
  146.           Dec(ix);
  147.           While (ix > 0) and (Buf^[ix] = ^M) DO
  148.             Dec(ix);
  149.         end
  150.         else
  151.         begin
  152.           Writeln('"', FileName, '" doesn''t seem to be a Text File');
  153.           ix := -1;
  154.         end;
  155.  
  156.       end; {lc<n and ix>0}
  157.     end {Maxread>0}
  158.     else
  159.       Lines := nil;
  160.     Dispose(Buf);
  161.   end
  162.   else
  163.     lc := -lc;
  164.  
  165.   if Lines <> nil then
  166.   begin
  167.     Lines^.ForEach(@DumpLine);
  168.     Dispose(Lines, Done);
  169.   end;
  170.  
  171.   ShowTail := lc;
  172.   FileMode := fm;
  173. end;
  174.  
  175. Function StripAll(Const Exclude : CharSet; S : String) : String;
  176. Var
  177.   ix : Integer;
  178. begin
  179.   ix := Length(S);
  180.   While ix > 0 DO
  181.   begin
  182.     if S[ix] in Exclude then
  183.       Delete(S, ix, 1);
  184.     Dec(ix);
  185.   end;
  186.   StripAll := S;
  187. end;
  188.  
  189. begin
  190.   if (ParamCount < 1) or (ParamCount > 2) then
  191.   begin
  192.     Writeln('TAIL v.1.0 - PD 1993 Lars Fosdal');
  193.     Writeln('  TAIL [d:\path]Filename.ext [-n]');
  194.     Writeln('  Default is 10 lines');
  195.   end
  196.   else
  197.   begin
  198.     if ParamCount = 2 then
  199.     begin
  200.       Val(StripAll(['/','-'], ParamStr(2)), l, e);
  201.       if e <> 0 then
  202.         l := 10
  203.     end
  204.     else
  205.       l := 10;
  206.  
  207.     r := ShowTail(ParamStr(1), l);
  208.     if r < 0 then
  209.     begin
  210.       Writeln('Couldn''t open "', ParamStr(1), '"!  (Error ', -r, ')');
  211.       Halt(Word(-r));
  212.     end;
  213.   end;
  214. end.
  215.